package trong.gold.soft.com.model.font;

import java.io.DataInputStream;
import java.io.InputStream;
import javax.microedition.lcdui.Graphics;
import javax.microedition.lcdui.Image;

/**
 * Bitmap font. Image and data of the font should be created with FontCreator
 * Last modified on January 29, 2009, 4:52 PM
 ** @author phongkstn
 */
public class MotivenBitmapFont extends MotivenFont {

    // Stores the widths of the characters: 26 letters, 10 digits, 12 symbols of !@#$%^&*()+-=/<>?.,:;'
    private int mWidths[];
    // Stores offset of the characters on the long image
    private int mImgOffset[];
    // Space width between the 2 continous words.
    private int mSpaceWidth;
    // Spacing width between the 2 continous characters.
    private int mSpacingWidth;
    // Stores the line spacing height.
    private int mLineSpacing;
    // private the height of the characters.
    private int mHeight;
    // The font's image comes here.
    private Image mImage;
    /**
     * The number of characters
     */
    private int mNumberCharacters;
    /**
     * Store code of characters, used with searching
     */
    private char mUnicodeCode[];
    /**
     * Index of the characters on image
     */
    private int mCharIndex[];
    /**
     * Is this font has outline or not?
     */
    private boolean mIsOutline;

    /**
     * Creating font with original color from the image
     * @param imgPath
     * @param dataPath
     * @param isOutline
     * @throws java.lang.Exception
     */
    public MotivenBitmapFont(String imgPath, String dataPath, boolean isOutline) {
        try {
            mIsOutline = isOutline;
            mImage = Image.createImage(imgPath);

            InputStream is = this.getClass().getResourceAsStream(dataPath);
            DataInputStream aDataInput = new DataInputStream(is);

            mNumberCharacters = aDataInput.readShort();
            mWidths = new int[mNumberCharacters];
            mUnicodeCode = new char[mNumberCharacters];
            mImgOffset = new int[mNumberCharacters];
            mCharIndex = new int[mNumberCharacters];

            mImgOffset[0] = 0;
            for (int i = 0; i < mNumberCharacters; i++) {
                mCharIndex[i] = i;
                mUnicodeCode[i] = aDataInput.readChar();
                mWidths[i] = aDataInput.readShort();
                // caculating offset of font image for each characters
                if (i > 0) {
                    mImgOffset[i] = mImgOffset[i - 1] + mWidths[i - 1] + 4;
                } else {
                    mImgOffset[i] = 4;
                }
            }

            if (mIsOutline) {
                for (int i = 0; i < mNumberCharacters; i++) {
                    mWidths[i] += 2;
                    mImgOffset[i] -= 1;
                }
            }

            mHeight = mImage.getHeight();
            mSpaceWidth = mWidths[0];
            if (mIsOutline) {
                mSpacingWidth = -1;
            } else {
                mSpacingWidth = 0;
            }
            //do sort for binary search
            for (int i = 0; i < mNumberCharacters - 1; i++) {
                for (int j = i + 1; j < mNumberCharacters; j++) {
                    if (mUnicodeCode[i] > mUnicodeCode[j]) {
                        char mid = mUnicodeCode[i];
                        mUnicodeCode[i] = mUnicodeCode[j];
                        mUnicodeCode[j] = mid;

                        int imid = mCharIndex[i];
                        mCharIndex[i] = mCharIndex[j];
                        mCharIndex[j] = imid;
                    }
                }
            }
            aDataInput.close();
        } catch (Exception e) {
            System.out.println("MotivenBitmapFont 1");
        };
    }

    /**
     * Creating font with customize color
     * @param imgPath
     * @param dataPath
     * @param isOutline is this font has outline or not
     * @param color
     * @param outlineColor
     * @throws java.lang.Exception
     */
    public MotivenBitmapFont(String imgPath, String dataPath, boolean isOutline, int color, int outlineColor) throws Exception {
        mIsOutline = isOutline;
        mImage = Image.createImage(imgPath);

        Image aImg = Image.createImage(mImage.getWidth(), mImage.getHeight());
        Graphics aG = aImg.getGraphics();
        aG.setColor(0xFF00FF);
        aG.fillRect(0, 0, aImg.getWidth(), aImg.getHeight());
        aG.drawImage(mImage, 0, 0, 0);

        int aARGB[] = new int[mImage.getWidth() * mImage.getHeight()];
        aImg.getRGB(aARGB, 0, mImage.getWidth(), 0, 0, mImage.getWidth(), mImage.getHeight());
        int aARGB2[] = new int[mImage.getWidth() * mImage.getHeight()];
        for (int i = 0; i < aARGB.length; i++) {
            if (!mIsOutline) {
                if (aARGB[i] == 0xFF000000) {
                    aARGB2[i] = color;
                } else {
                    aARGB2[i] = 0x0;
                }
            } else {
                if (aARGB[i] == 0xFFFFFFFF) {
                    aARGB2[i] = color;
                } else if (aARGB[i] == 0xFFFF0000) {
                    aARGB2[i] = outlineColor;
                } else {
                    aARGB2[i] = 0x0;
                }
            }
        }
        mImage = Image.createRGBImage(aARGB2, mImage.getWidth(), mImage.getHeight(), true);

        InputStream is = this.getClass().getResourceAsStream(dataPath);
        DataInputStream aDataInput = new DataInputStream(is);

        mNumberCharacters = aDataInput.readShort();
        mWidths = new int[mNumberCharacters];
        mUnicodeCode = new char[mNumberCharacters];
        mImgOffset = new int[mNumberCharacters];
        mCharIndex = new int[mNumberCharacters];

        mImgOffset[0] = 0;
        for (int i = 0; i < mNumberCharacters; i++) {
            mCharIndex[i] = i;
            mUnicodeCode[i] = aDataInput.readChar();
            mWidths[i] = aDataInput.readShort();
            // caculating offset of font image for each characters
            if (i > 0) {
                mImgOffset[i] = mImgOffset[i - 1] + mWidths[i - 1] + 4;
            } else {
                mImgOffset[i] = 4;
            }
        }

        if (mIsOutline) {
            for (int i = 0; i < mNumberCharacters; i++) {
                mWidths[i] += 2;
                mImgOffset[i] -= 1;
            }
        }

        mHeight = mImage.getHeight();
        mSpaceWidth = mWidths[0];
        if (mIsOutline) {
            mSpacingWidth = -1;
        } else {
            mSpacingWidth = 0;
        }
        //do sort for binary search
        for (int i = 0; i < mNumberCharacters - 1; i++) {
            for (int j = i + 1; j < mNumberCharacters; j++) {
                if (mUnicodeCode[i] > mUnicodeCode[j]) {
                    char mid = mUnicodeCode[i];
                    mUnicodeCode[i] = mUnicodeCode[j];
                    mUnicodeCode[j] = mid;

                    int imid = mCharIndex[i];
                    mCharIndex[i] = mCharIndex[j];
                    mCharIndex[j] = imid;
                }
            }
        }
        aDataInput.close();
    }

    /**
     * Gets the width of a specified character.
     * @param c the specified character
     * @return the width of the input character
     */
    public int getWidth(char c) {
        int index = getIndex(c);
        if (index == -1) {
            return mSpaceWidth;
        }
        return mWidths[index];
    }

    /**
     * Gets the width of a specified text.
     * @param text the specified text
     * @return the width of the input text
     */
    public  int getWidth(String text) {
        int width = 0;
        int length = text.length();

        for (int i = 0; i < length; i++) {
            width += (getWidth(text.charAt(i)) + mSpacingWidth);
        }

        return width;
    }

    /**
     * Gets the width of a sub string of the given text.
     * @param text  the given text
     * @param start the start position
     * @param end   the end position
     * @return the width of the sub string
     */
    public int getSubWidth(String text, int start, int end) {
        int width = 0;
        for (int i = start; i < end; i++) {
            width += getWidth(text.charAt(i));
        }

        return width;
    }

    /**
     * Gets the space width between the 2 contious words.
     * @return the space width
     */
    public int getSpaceWidth() {
        return mSpaceWidth;
    }

    /**
     * Gets the spacing width between the 2 continous characters.
     * @return the spacing width
     */
    public int getSpacingWidth() {
        return mSpacingWidth;
    }

    /**
     * Gets the line spacing height.
     * @return the line spacing height
     */
    public int getLineSpacing() {
        return mLineSpacing;
    }

    /**
     * Gets the height of this font.
     * @return the height of the font
     */
    public int getHeight() {
        return mHeight;
    }

    public void DrawSubString(Graphics g, String text, int index, int length, int x, int y, int align) {

        if (text == null || length < 0 || index + length > text.length()) {
            return;
        }

        int tx = x,
                ty = y;
        if ((align & Graphics.HCENTER) == Graphics.HCENTER) {
            tx -= (getSubWidth(text, index, length) >> 1);
        } else if ((align & Graphics.RIGHT) == Graphics.RIGHT) {
            tx -= getSubWidth(text, index, length);
        }
        if ((align & Graphics.VCENTER) == Graphics.VCENTER) {
            ty -= (getHeight() >> 1);
        } else if ((align & Graphics.BOTTOM) == Graphics.BOTTOM) {
            ty -= getHeight();
        }

        int cx = g.getClipX(),
                cy = g.getClipY(),
                cw = g.getClipWidth(),
                ch = g.getClipHeight();

        int cx2 = cx + cw;
        int cy2 = cy + ch;

        //int length = text.length();

        int charWidth;
        int charOffset;
        char c;
        int char_index;

        int fCX;
        int fCY;
        int fCW;
        int fCH;
        int fCX2;
        int fCY2;
        length += index;
        for (int i = index; i < length; i++) {
            c = text.charAt(i);
            char_index = getIndex(c);

            // cannot recognize, treat it as space
            if (char_index == -1) {
                c = ' ';
                char_index = 0;
            }

            charWidth = mWidths[char_index];
            charOffset = mImgOffset[char_index];

            // skip the space
            if (c != ' ') {
                fCX = Math.max(cx, tx);
                fCY = Math.max(cy, ty);
                fCX2 = Math.min(tx + charWidth, cx2);
                fCY2 = Math.min(ty + getHeight(), cy2);
                fCW = fCX2 - fCX;
                fCH = fCY2 - fCY;
                g.setClip(fCX, fCY, fCW, fCH);
                g.drawImage(mImage, tx - charOffset, ty, 0);
            }

            tx += charWidth + mSpacingWidth;
        }

        g.setClip(cx, cy, cw, ch); // restore the clip
    }

    /**
     * Render a text
     * @param g
     * @param text
     * @param x
     * @param y
     * @param align
     */
    public void DrawString(Graphics g, String text, int x, int y, int align) {
        if (text == null) {
            return;
        }

        int tx = x,
                ty = y;
        if ((align & Graphics.HCENTER) == Graphics.HCENTER) {
            tx -= (getWidth(text) >> 1);
        } else if ((align & Graphics.RIGHT) == Graphics.RIGHT) {
            tx -= getWidth(text);
        }
        if ((align & Graphics.VCENTER) == Graphics.VCENTER) {
            ty -= (getHeight() >> 1);
        } else if ((align & Graphics.BOTTOM) == Graphics.BOTTOM) {
            ty -= getHeight();
        }

        int cx = g.getClipX(),
                cy = g.getClipY(),
                cw = g.getClipWidth(),
                ch = g.getClipHeight();

        int cx2 = cx + cw;
        int cy2 = cy + ch;

        int length = text.length();

        int charWidth;
        int charOffset;
        char c;
        int char_index;

        int fCX;
        int fCY;
        int fCW;
        int fCH;
        int fCX2;
        int fCY2;

        for (int i = 0; i < length; i++) {
            c = text.charAt(i);
            char_index = getIndex(c);

            // cannot recognize, treat it as space
            if (char_index == -1) {
                c = ' ';
                char_index = 0;
            }

            charWidth = mWidths[char_index];
            charOffset = mImgOffset[char_index];
            // skip the space
            if (c != ' ') {
                fCX = Math.max(cx, tx);
                fCY = Math.max(cy, ty);
                fCX2 = Math.min(tx + charWidth, cx2);
                fCY2 = Math.min(ty + getHeight(), cy2);
                fCW = fCX2 - fCX;
                fCH = fCY2 - fCY;
                g.setClip(fCX, fCY, fCW, fCH);
                g.drawImage(mImage, tx - charOffset, ty, 0);
            }

            tx += charWidth + mSpacingWidth;
        }

        g.setClip(cx, cy, cw, ch); // restore the clip
    }

    /**
     * Retrieves the order of the specified character in the image. Internal used only.
     * @param c the specified character
     * @return the order of the character in the image
     */
    private int getIndex(char c) {
        // binary search
        int start = 0;
        int end = mNumberCharacters;
        int mid;
        while (start < end - 1) {
            mid = (end + start) / 2;
            if (mUnicodeCode[mid] > c) {
                end = mid;
            } else if (mUnicodeCode[mid] < c) {
                start = mid;
            } else {
                return mCharIndex[mid];
            }
        }

        if (mUnicodeCode[start] == c) {
            return mCharIndex[start];
        }

        return -1; //not found
    }

    public void DrawChar(Graphics g, char aChar, int x, int y, int align) {


        int tx = x,
                ty = y;
        if ((align & Graphics.HCENTER) == Graphics.HCENTER) {
            tx -= (getWidth(aChar) >> 1);
        } else if ((align & Graphics.RIGHT) == Graphics.RIGHT) {
            tx -= getWidth(aChar);
        }
        if ((align & Graphics.VCENTER) == Graphics.VCENTER) {
            ty -= (getHeight() >> 1);
        } else if ((align & Graphics.BOTTOM) == Graphics.BOTTOM) {
            ty -= getHeight();
        }

        int cx = g.getClipX(),
                cy = g.getClipY(),
                cw = g.getClipWidth(),
                ch = g.getClipHeight();

        int cx2 = cx + cw;
        int cy2 = cy + ch;


        int charWidth;
        int charOffset;
        int char_index;

        int fCX;
        int fCY;
        int fCW;
        int fCH;
        int fCX2;
        int fCY2;


        char_index = getIndex(aChar);

        // cannot recognize, treat it as space
        if (char_index == -1) {
            aChar = ' ';
            char_index = 0;
        }

        charWidth = mWidths[char_index];
        charOffset = mImgOffset[char_index];

        // skip the space
        if (aChar != ' ') {
            fCX = Math.max(cx, tx);
            fCY = Math.max(cy, ty);
            fCX2 = Math.min(tx + charWidth, cx2);
            fCY2 = Math.min(ty + getHeight(), cy2);
            fCW = fCX2 - fCX;
            fCH = fCY2 - fCY;
            g.setClip(fCX, fCY, fCW, fCH);
            g.drawImage(mImage, tx - charOffset, ty, 0);
        }

        //tx += charWidth + mSpacingWidth;

        g.setClip(cx, cy, cw, ch); // restore the clip
    }
}
